﻿/**
 * Peter
 * Created by: Peter Development Team
 *    http://peter.codeplex.com/
 * 
 * GNU General Public License version 2 (GPLv2)
 *    http://peter.codeplex.com/license
 **/

using System;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.Linq;
using System.Windows.Media;
using System.Xml.Serialization;
using Peter.Common;
using Peter.Common.Interfaces;
using Peter.Common.MainMenu;
using Peter.Common.Utilities;

namespace Peter.Data
{
   /// <summary>
   /// Represents the options for the avalon text edit control.
   /// </summary>
   [Export (typeof (IOptionPage))]
   [PartCreationPolicy (CreationPolicy.Shared)]
   public class OptionsEditor : ViewModelBase, IOptionPage, IPartImportsSatisfiedNotification
   {
      #region Fields

      private int m_IndentSize;
      private bool m_HighlightCurrentLine;
      private bool m_ShowLineNumbers;
      private bool m_WrapText;
      private bool m_EnableEmails;
      private bool m_EnableHyperlinks;
      private bool m_ShowTabs;
      private bool m_ShowSpaces;
      private bool m_ShowEol;
      private bool m_ShowVerticalRuler;
      private bool m_ConvertTabsToSpaces;
      private bool m_AllowScrollPastDocument;
      private bool m_HoldCtrlForHyperlinks;
      private FontFamily m_SelectedFont;
      private string m_FontName;
      private double m_FontSize;
      private bool m_CodeFolding;
      private int m_VerticalRulerColumn;

      #endregion

      #region Setup

      /// <summary>
      /// Initializes a new global editor properties object.
      /// </summary>
      public OptionsEditor () 
      { 
         this.SetDefaults ();
         this.HighlightingExtensions = new Collection<HighlightExtension> ();
      }

      /// <summary>
      /// Sets the default properties.
      /// </summary>
      public void SetDefaults ()
      {
         this.ShowLineNumbers = true;
         this.WrapText = false;
         this.EnableEmails = false;
         this.EnableHyperlinks = false;
         this.ShowTabs = false;
         this.ShowSpaces = false;
         this.ShowEol = false;
         this.IndentSize = 3;
         this.ConvertTabsToSpaces = true;
         this.AllowScrollPastDocument = true;
         this.HoldCtrlForHyperlinks = true;
         this.FontName = "Courier New";
         this.FontSize = 12;
         this.m_CodeFolding = true;
         this.VerticalRulerColumn = 80;
      }

      #endregion

      #region Properties

      /// <summary>
      /// Gets the peter data.
      /// </summary>
      [XmlIgnore]
      [Import (typeof (IPeterData))]
      public IPeterData PeterData { get; set; }

      /// <summary>
      /// Gets or Sets if the editors should show line numbers or not.
      /// </summary>
      public bool ShowLineNumbers 
      {
         get { return this.m_ShowLineNumbers; }
         set
         {
            this.m_ShowLineNumbers = value;
            this.OnPropertyChanged ("ShowLineNumbers");
         }
      }

      /// <summary>
      /// Gets or Sets if the editors should highlight the current line or not.
      /// </summary>
      public bool HighlightCurrentLine
      {
         get { return this.m_HighlightCurrentLine; }
         set
         {
            this.m_HighlightCurrentLine = value;
            this.OnPropertyChanged ("HighlightCurrentLine");
         }
      }

      /// <summary>
      /// Gets or Sets if the vertical ruler should be shown or not.
      /// </summary>
      public bool ShowVerticalRuler
      {
         get { return this.m_ShowVerticalRuler; }
         set
         {
            this.m_ShowVerticalRuler = value;
            this.OnPropertyChanged ("ShowVerticalRuler");
         }
      }

      /// <summary>
      /// Gets or Sets the column for the vertical ruler.
      /// </summary>
      public int VerticalRulerColumn
      {
         get { return this.m_VerticalRulerColumn; }
         set
         {
            this.m_VerticalRulerColumn = value;
            this.OnPropertyChanged ("VerticalRulerColumn");
         }
      }

      /// <summary>
      /// Gets or Sets if the editors should word wrap or not.
      /// </summary>
      public bool WrapText
      {
         get { return this.m_WrapText; }
         set
         {
            this.m_WrapText = value;
            this.OnPropertyChanged ("WrapText");
         }
      }

      /// <summary>
      /// Gets or Sets if ctrl should be held to follow hyperlinks or not.
      /// </summary>
      public bool HoldCtrlForHyperlinks
      {
         get { return this.m_HoldCtrlForHyperlinks; }
         set
         {
            this.m_HoldCtrlForHyperlinks = value;
            this.OnPropertyChanged ("HoldCtrlForHyperlinks");
         }
      }

      /// <summary>
      /// Gets or Sets if the editor is allowed to scroll past the end of the document or not.
      /// </summary>
      public bool AllowScrollPastDocument
      {
         get { return this.m_AllowScrollPastDocument; }
         set
         {
            this.m_AllowScrollPastDocument = value;
            this.OnPropertyChanged ("AllowScrollPastDocument");
         }
      }

      /// <summary>
      /// Gets or Sets if the editors should convert tabs to spaces or not.
      /// </summary>
      public bool ConvertTabsToSpaces
      {
         get { return this.m_ConvertTabsToSpaces; }
         set
         {
            this.m_ConvertTabsToSpaces = value;
            this.OnPropertyChanged ("ConvertTabsToSpaces");
         }
      }

      /// <summary>
      /// Gets or Sets the editors indention size.
      /// </summary>
      public int IndentSize
      {
         get { return this.m_IndentSize; }
         set
         {
            this.m_IndentSize = value;
            this.OnPropertyChanged ("IndentSize");
         }
      }

      /// <summary>
      /// Gets or Sets if the editors should show end of line symbols or not.
      /// </summary>
      public bool ShowEol
      {
         get { return this.m_ShowEol; }
         set
         {
            this.m_ShowEol = value;
            this.OnPropertyChanged ("ShowEOL");
         }
      }

      /// <summary>
      /// Gets or Sets if the editors should show space symbols or not.
      /// </summary>
      public bool ShowSpaces
      {
         get { return this.m_ShowSpaces; }
         set
         {
            this.m_ShowSpaces = value;
            this.OnPropertyChanged ("ShowSpaces");
         }
      }

      /// <summary>
      /// Gets or Sets if the editors should show tab symbols or not.
      /// </summary>
      public bool ShowTabs
      {
         get { return this.m_ShowTabs; }
         set
         {
            this.m_ShowTabs = value;
            this.OnPropertyChanged ("ShowTabs");
         }
      }

      /// <summary>
      /// Gets or Sets if the editors should highlight hyperlinks or not.
      /// </summary>
      public bool EnableHyperlinks
      {
         get { return this.m_EnableHyperlinks; }
         set
         {
            this.m_EnableHyperlinks = value;
            this.OnPropertyChanged ("EnableHyperlinks");
         }
      }

      /// <summary>
      /// Gets or Sets if the editors should highlight emails or not.
      /// </summary>
      public bool EnableEmails
      {
         get { return this.m_EnableEmails; }
         set
         {
            this.m_EnableEmails = value;
            this.OnPropertyChanged ("EnableEmails");
         }
      }

      /// <summary>
      /// Gets or Sets the name of the editor's font.
      /// </summary>
      public string FontName
      {
         get { return this.m_FontName; }
         set
         {
            if (this.m_FontName != value)
            {
               this.m_FontName = value;
               this.OnPropertyChanged ("FontName");
               this.SetSelectedFont ();
            }
         }
      }

      /// <summary>
      /// Sets the selected font;
      /// </summary>
      private void SetSelectedFont ()
      {
         if ((this.SelectedFont == null || this.SelectedFont.Source != this.m_FontName) &&
             !string.IsNullOrEmpty (this.m_FontName))
         {
            try
            {
               this.SelectedFont = new FontFamily (this.m_FontName);
            }
            catch (Exception ex)
            {
               this.SelectedFont = new FontFamily ("Arial");
               Console.Error.WriteLine (ex.Message);
            }
         }
      }

      /// <summary>
      /// Gets or Sets the font size.
      /// </summary>
      public double FontSize
      {
         get { return this.m_FontSize; }
         set
         {
            if (!this.m_FontSize.Equals (value))
            {
               this.m_FontSize = value;
               this.OnPropertyChanged ("FontSize");
            }
         }
      }

      /// <summary>
      /// Gets the name of the option page.
      /// </summary>
      [XmlIgnore]
      public string Name
      {
         get { return "Editor"; }
      }

      /// <summary>
      /// Gets the icon of the option page.
      /// </summary>
      [XmlIgnore]
      public MainMenuIcon OptionPageIcon
      {
         get { return MainMenuIcon.Editalt; }
      }

      /// <summary>
      /// Gets the selected font.
      /// </summary>
      [XmlIgnore]
      public FontFamily SelectedFont
      {
         get { return this.m_SelectedFont; }
         set
         {
            if (!Equals (this.m_SelectedFont, value))
            {
               this.m_SelectedFont = value;
               this.OnPropertyChanged ("SelectedFont");
               this.FontName = this.m_SelectedFont.Source;
            }
         }
      }

      /// <summary>
      /// Gets or Sets if the code folding flag.
      /// </summary>
      public bool CodeFolding
      {
          get { return this.m_CodeFolding; }
          set
          {
              this.m_CodeFolding = value;
              this.OnPropertyChanged("CodeFolding");
          }
      }

      /// <summary>
      /// Gets or Sets the list of highlighting extensions.
      /// </summary>
      public Collection<HighlightExtension> HighlightingExtensions { get; set; }

      /// <summary>
      /// Loads the data from xml.
      /// </summary>
      /// <param name="xmlToLoad">Xml string to load.</param>
      public void LoadFromXml (string xmlToLoad)
      {
         OptionsEditor loadedData = null;
         try
         {
            loadedData = Serialization.DeserializeFromString <OptionsEditor> (xmlToLoad);
         }
         catch (Exception ex)
         {
            Console.Error.WriteLine (ex.Message);
         }

         if (loadedData != null)
            this.CopyPropertiesFrom (loadedData);
      }

      /// <summary>
      /// Saves any data to xml.
      /// </summary>
      /// <returns>The data to save in Xml format.</returns>
      public string SaveToXml ()
      {
         return this.Serialize ();
      }

      #endregion

      /// <summary>
      /// Adds the given extension for the given definition.
      /// </summary>
      /// <param name="definition">The name of the highlighting definition.</param>
      /// <param name="extension">The extension to add.</param>
      public void AddHighlightingExtension (string definition, string extension)
      {
         var highlightExt = this.HighlightingExtensions.FirstOrDefault (h => h.Definition == definition);
         if (highlightExt == null) {
            highlightExt = new HighlightExtension {Definition = definition};
            this.HighlightingExtensions.Add (highlightExt);
         }
         if (!highlightExt.Extensions.Contains (extension))
            highlightExt.Extensions.Add (extension);
      }

      /// <summary>Called when a part's imports have been satisfied and it is safe to use.</summary>
      public void OnImportsSatisfied ()
      {
         var type = this.GetType ().ToString ();
         this.LoadFromXml (this.PeterData.RequestData (type));
      }
   }
}
